Data

acc1_cancer_cells = readRDS("./Data/acc1_cancer_cells_15KnCount_V3.RDS")
all_acc_cancer_cells = readRDS("./Data/acc_cancer_cells_V3.RDS")
acc_all_cells = readRDS("./Data/acc_tpm_nCount_mito_no146_15k_with_ACC1_.RDS")
luminal_pathways = c("CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_UP","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_UP","HUPER_BREAST_BASAL_VS_LUMINAL_DN","LIM_MAMMARY_LUMINAL_PROGENITOR_UP","SMID_BREAST_CANCER_LUMINAL_B_UP" )

# add luminal pathways
luminal_gs = msigdbr(species = "Homo sapiens") %>%as.data.frame() %>% dplyr::filter(gs_name %in% luminal_pathways)%>% dplyr::distinct(gs_name, gene_symbol) %>% as.data.frame()

Parameters

suffix = "01_25"
data_to_read = ""

functions

source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "HMSC_functions",version = "0.1.11",script_name = "functions.R")
source_from_github(repositoy = "cNMF_functions",version = "0.3.53",script_name = "cnmf_function_Harmony.R")

Enrichment analysis HMSC vs ACC

patient.ident = all_acc_cancer_cells$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells = AddMetaData(object = all_acc_cancer_cells,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells = SetIdent(all_acc_cancer_cells, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

cell cycle filtering

hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets  =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=all_acc_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(all_acc_cancer_cells@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
all_acc_cancer_cells=AddMetaData(all_acc_cancer_cells,score,hallmark_name)

#filter:
all_acc_cancer_cells_ccFiltered=all_acc_cancer_cells[,all_acc_cancer_cells@meta.data[[hallmark_name]]< 0.3]


min_threshold = min(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
max_threshold = max(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)

Before cc filtering

FeaturePlot(object = all_acc_cancer_cells,features = hallmark_name) + ggtitle("Before cc filtering")  & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

After cc filtering

FeaturePlot(object = all_acc_cancer_cells_ccFiltered,features = hallmark_name) + ggtitle("After cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Enrichment analysis filtered HMSC vs ACC

patient.ident = all_acc_cancer_cells_ccFiltered$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells_ccFiltered = AddMetaData(object = all_acc_cancer_cells_ccFiltered,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells_ccFiltered = SetIdent(all_acc_cancer_cells_ccFiltered, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells_ccFiltered, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells_ccFiltered))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells_ccFiltered),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

MYB expression

all_acc_cancer_cells = SetIdent(object = all_acc_cancer_cells,value = "patient.ident") #active snn graph
FeaturePlot(object = all_acc_cancer_cells,features = "MYB",label = T)

CNV

new.cluster.ids <- c("cancer", #0
                     "cancer", #1
                     "CAF", #2
                     "cancer", #3
                     "Endothelial", #4
                     "cancer", #5
                     "cancer", #6
                     "CAF", #7
                     "CAF", #8
                     "CAF", #9
                     "cancer", #10
                     "CAF", #11
                     "cancer", #12
                     "cancer", #13
                     "cancer", #14
                     "cancer", #15
                     "cancer", #16
                     "WBC", #17
                     "CAF" #18
                     )
#rename idents:
acc_all_cells = SetIdent(object = acc_all_cells,value = "RNA_snn_res.1") #active snn graph
names(new.cluster.ids) <- levels(acc_all_cells) #add snn graph levels to new.cluster.ids
acc_all_cells@meta.data[["seurat_clusters"]] = acc_all_cells@meta.data[["RNA_snn_res.1"]]
acc_all_cells = SetIdent(object = acc_all_cells,value = "seurat_clusters")
acc_all_cells <- RenameIdents(acc_all_cells, new.cluster.ids) 

# divide "cancer" into patients:
cell_types = acc_all_cells@active.ident %>% as.data.frame()
cell_types[,1]<- as.character(cell_types[,1])
cell_types = cbind(cell_types,acc_all_cells$patient.ident) %>% setnames(old = names(.), 
         new = c('cell_type','patient'))
cell_types[cell_types$cell_type == "cancer",] = cell_types[cell_types$cell_type == "cancer",2]


# hmsc_rows = (startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer" 
# acc_rows = !(startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer" 
# cell_types[,1][hmsc_rows]  = "HMSC"
# cell_types[,1][acc_rows]  = "ACC"

#add to metadata:
cell_types[,2] = NULL 
cell_types[cell_types$cell_type == "ACC1",] = "HMSC"
acc_all_cells = AddMetaData(object =acc_all_cells ,metadata = cell_types,col.name = "cell.type")

CNV UMAP

CNV plot

CNV plot ## {-}

CNV subtypes

cnv_subtypes = as.data.frame(cutree(infercnv_obj_default@tumor_subclusters[["hc"]][["HMSC"]], k = 2))
names(cnv_subtypes)[1] = "cnv.cluster"
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "-",replacement = "\\.")
infercnv.observations = data.frame(fread(file = "./Data/inferCNV/infercnv.observations.txt"), row.names=1)
Warning in fread(file = "./Data/inferCNV/infercnv.observations.txt") :
  Detected 1332 column names but the data has 1333 columns (i.e. invalid file). Added 1 extra default column name for the first column which is guessed to be row names or an index. Use setnames() afterwards if this guess is not correct, or fix the file write command that created the file to create a valid file.
names_to_keep = colnames(infercnv.observations) %in% (colnames(acc1_cancer_cells) %>% gsub(pattern = "_",replacement = "\\."))
infercnv.observations = infercnv.observations[,names_to_keep]
rotate <- function(x) t(apply(x, 2, rev))
infercnv.observations2 = infercnv.observations %>% rotate() %>%  rotate() %>% rotate()%>% as.data.frame() 
breaks = c(0.700891861704857,
0.742366945528369,
0.783842029351881,
0.825317113175393,
0.866792196998905,
0.908267280822417,
0.949742364645928,
0.99121744846944,
1.03269253229295,
1.07416761611646,
1.11564269993998,
1.15711778376349,
1.198592867587,
1.24006795141051,
1.28154303523402,
1.32301811905753)
pheatmap(infercnv.observations2,cluster_cols = F,cluster_rows = F, show_rownames = F,show_colnames = F, breaks = breaks,color = colorRampPalette(rev(c("darkred", "white", "darkblue")))(15),annotation_row = cnv_subtypes)

rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "2\\.",replacement = "2_")
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "3\\.",replacement = "3_")
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = cnv_subtypes)
DimPlot(acc1_cancer_cells,group.by = "cnv.cluster",pt.size = 2,cols =colors)

Original score

original_myo_genes = c( "TP63", "TP73", "CAV1", "CDH3", "KRT5", "KRT14", "ACTA2", "TAGLN", "MYLK", "DKK3")
original_lum_genes = c("KIT", "EHF", "ELF5", "KRT7", "CLDN3", "CLDN4", "CD24", "LGALS3", "LCN2", "SLPI" )
calculate_score(dataset = all_acc_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes)
correlation of lum score and myo score: -0.45
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1

Original score of ACC1

calculate_score(dataset = acc1_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: 0.06
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1

0.35 Most correlated score

Myo genes

myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo  = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.35)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes =  20"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
 [1] "COL16A1"     "RP1-39G22.4" "ACTG2"       "CD200"       "MYLK"        "TP63"        "KCNMB1"      "ADAMTS2"    
 [9] "LOXL2"       "TPM2"        "CLIC3"       "SNCG"        "ACTA2"       "TAGLN"       "A2M"         "NGFR"       
[17] "CNN1"        "PPP1R14A"    "MYL9"        "POM121L9P"  
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes) 
[1] "MYLK"  "TP63"  "ACTA2" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = rownames(acc1_cancer_cells),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res

Lum genes

lum_protein_markers = c("KIT")
top_lum  = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.35)
Warning in cor(expression %>% t(), markers_average) :
  the standard deviation is zero
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes =  22"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
 [1] "FBXO44"        "USP48"         "MPC2"          "SLC19A2"       "ATL2"          "B3GNT2"        "AC093162.5"   
 [8] "MAP2"          "KIT"           "GLRB"          "EFNA5"         "PCDHGB7"       "SLC29A1"       "SASH1"        
[15] "ALDH3B2"       "CCND1"         "RP11-254B13.1" "VSIG10"        "NDFIP2"        "SUSD6"         "SPPL2A"       
[22] "DSC2"         
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes) 
[1] "KIT"
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = rownames(acc1_cancer_cells),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res

top correlated score

calculate_score(dataset = acc1_cancer_cells,myo_genes = top_myo,lum_genes = top_lum,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: -0.05
correlation of lum score and original lum score: 0.58
correlation of myo score and original myo score: 0.77

enriched genes score

rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = -2.5,myo_threshold = -2.5)
correlation of lum score and myo score: -0.16
correlation of lum score and original lum score: 0.43
correlation of myo score and original myo score: 0.79

0.2 Most correlated score

myo Genes

myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo  = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.2)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes =  473"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
 [1] "MIR429"        "EXOSC10"       "PLOD1"         "TNFRSF8"       "PDPN"          "FBLIM1"        "RP11-276H7.3" 
 [8] "HSPG2"         "EPHA8"         "IFNLR1"        "GRHL3"         "RP3-426I6.6"   "SDC3"          "RP11-490K7.1" 
[15] "RP11-73M7.1"   "TINAGL1"       "COL16A1"       "RP1-117O3.2"   "RP11-435D7.3"  "RP1-39G22.4"   "SCMH1"        
[22] "RP5-850O15.4"  "RP5-866L20.3"  "NEXN"          "RP4-714D9.2"   "RP5-837M10.3"  "RP5-964H19.1"  "CSF1"         
[29] "RP11-498A13.1" "RP11-96K19.2" 
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes) 
[1] "MYLK"  "TP63"  "ACTA2" "DKK3"  "TAGLN" "CDH3" 
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = rownames(acc1_cancer_cells),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res

Lum Genes

lum_protein_markers = c("KIT")
top_lum  = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.2)
Warning in cor(expression %>% t(), markers_average) :
  the standard deviation is zero
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes =  1535"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
 [1] "RP11-206L10.11" "HES4"           "RP11-54O7.18"   "CPTP"           "RP3-395M20.8"   "TNFRSF14"      
 [7] "RPL22"          "ICMT"           "ERRFI1"         "RERE"           "H6PD"           "TMEM201"       
[13] "NMNAT1"         "UBE4B"          "APITD1-CORT"    "CENPS"          "RP4-635E18.7"   "FBXO2"         
[19] "FBXO44"         "UQCRHL"         "MST1P2"         "SDHB"           "PADI2"          "ARHGEF10L"     
[25] "AKR7A2"         "TMCO4"          "EIF4G3"         "USP48"          "NIPAL3"         "PIGV"          
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes) 
[1] "KIT"    "EHF"    "LGALS3"
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = rownames(acc1_cancer_cells),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
calculate_score = function(dataset,myo_genes,lum_genes,lum_threshold =1 , myo_threshold = -1) {
  myoscore=apply(dataset@assays[["RNA"]][myo_genes,],2,mean)
  lescore=apply(dataset@assays[["RNA"]][lum_genes,],2,mean)
  correlation = cor(lescore,myoscore) %>% round(digits = 2)
  message("correlation of lum score and myo score:" %>% paste(correlation))
  
  
  original_myo_genes = c("TP63", "TP73", "CAV1", "CDH3", "KRT5", "KRT14", "ACTA2", "TAGLN", "MYLK", "DKK3")
  original_lum_genes = c("KIT", "EHF", "ELF5", "KRT7", "CLDN3", "CLDN4", "CD24", "LGALS3", "LCN2", "SLPI")
  orig_myoscore=apply(dataset@assays[["RNA"]][original_myo_genes,],2,mean)
  orig_lescore=apply(dataset@assays[["RNA"]][original_lum_genes,],2,mean)
  correlation_to_original_lum = cor(orig_lescore,lescore) %>% round(digits = 2)
  correlation_to_original_myo = cor(orig_myoscore,myoscore) %>% round(digits = 2)

  message("correlation of lum score and original lum score:" %>% paste(correlation_to_original_lum))
  message("correlation of myo score and original myo score:" %>% paste(correlation_to_original_myo))

  dataset=AddMetaData(dataset,lescore-myoscore,"luminal_over_myo")
  print(
    FeaturePlot(object = dataset,features = "luminal_over_myo")
  )
  data = FetchData(object = dataset,vars = "luminal_over_myo")
  print(
    data %>% 
    ggplot(aes( x=luminal_over_myo)) + 
    geom_density() 
    )
  
lum_cells_num = subset(x = dataset,luminal_over_myo >(lum_threshold)) %>% ncol() /ncol(dataset)
myo_cells_num = subset(x = dataset,luminal_over_myo <(myo_threshold)) %>% ncol()/ncol(dataset)
df = data.frame(cell_type = c("myo_cells","lum_cells"),percentage = c(myo_cells_num,lum_cells_num))
ggplot(data=df, aes(x=cell_type, y=percentage)) +
  geom_bar(stat="identity") + ggtitle("ACC cell types")
}
myo_intersected = intersect(top_myo,original_myo_genes) 
lum_intersected = intersect(top_lum,original_lum_genes) 
message("genes in myo score:")
genes in myo score:
myo_intersected
[1] "MYLK"  "TP63"  "ACTA2" "DKK3"  "TAGLN" "CDH3" 
message("genes in lum score:")
genes in lum score:
lum_intersected
[1] "KIT"    "EHF"    "LGALS3"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_intersected,lum_genes = lum_intersected,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: 0.02
correlation of lum score and original lum score: 0.79
correlation of myo score and original myo score: 0.89

enriched genes

rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
 [1] "FBLIM1"  "NEXN"    "NMNAT2"  "MYLK"    "CCDC50"  "IGFBP7"  "RAI14"   "ARAP3"   "SPARC"   "CALD1"   "LOXL2"  
[12] "COL5A1"  "ACTA2"   "DKK3"    "MSRB3"   "COL4A1"  "ACTN1"   "TPM1"    "TGFB1I1" "ADORA2B" "MXRA7"   "CNN1"   
[23] "TP63"    "ACTA2"  
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
 [1] "PADI2"      "PATJ"       "PEX11B"     "APH1A"      "C1orf43"    "EFNA4"      "NECTIN4"    "SCYL3"      "ELF3"      
[10] "SOX13"      "IRF6"       "MED28"      "EPB41L4A"   "RGL2"       "C6orf132"   "TPD52L1"    "ICA1"       "MACC1"     
[19] "TRPS1"      "FAM83H"     "RASEF"      "ARRDC1"     "COMMD3"     "ANK3"       "GSTO2"      "PDCD4"      "EHF"       
[28] "ALDH3B2"    "SHANK2"     "SORL1"      "FKBP4"      "PTPN6"      "DUSP16"     "RERG"       "ADCY6"      "ERBB3"     
[37] "ERP29"      "SUSD6"      "RPS6KA5"    "SPINT1"     "FEM1B"      "TLE3"       "SCAMP2"     "CLN3"       "ADGRG1"    
[46] "ATP2C2"     "GGT6"       "MYO1D"      "ST6GALNAC2" "CYB5A"      "BLVRB"      "VRK3"       "SYCP2"      "TMPRSS2"   
[55] "LIMK2"      "KIT"       
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 0,myo_threshold = -1)
correlation of lum score and myo score: 0.1
correlation of lum score and original lum score: 0.71
correlation of myo score and original myo score: 0.76

enriched genes and in original score

myo_enriched_genes = myo_enriched_genes[myo_enriched_genes %in% original_myo_genes]
lum_enriched_genes = lum_enriched_genes[lum_enriched_genes %in% original_lum_genes]

message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "MYLK"  "ACTA2" "DKK3"  "TP63"  "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "EHF" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 2,myo_threshold = 2)
correlation of lum score and myo score: -0.07
correlation of lum score and original lum score: 0.62
correlation of myo score and original myo score: 0.77

HPV

Only HMSC cancer cells:

HPV33_P3 = fread("./Data/HPV33_P3.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P3.df = HPV33_P3 %>% mutate(
  plate = gsub(x =HPV33_P3$plate, replacement = "",pattern = "_.*$") 
  %>% gsub(pattern = "-P",replacement = ".P") 
  %>% gsub(pattern = "-",replacement = "_",)
  )
HPV33_P3.df = HPV33_P3.df %>% dplyr::filter(HPV33_P3.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P3.df)  <- HPV33_P3.df$plate
HPV33_P3.df$plate = NULL


HPV33_P2 = fread("./Data/HPV33_P2.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P2.df = HPV33_P2 %>% mutate(
  plate = gsub(x =HPV33_P2$plate, replacement = "",pattern = "_.*$") 
  %>% gsub(pattern = "plate2-",replacement = "plate2_",)
  %>% gsub(pattern = "-",replacement = "\\.",)
  )
HPV33_P2.df = HPV33_P2.df %>% dplyr::filter(HPV33_P2.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P2.df)  <- HPV33_P2.df$plate
HPV33_P2.df$plate = NULL

HPV33 = rbind(HPV33_P3.df,HPV33_P2.df)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = HPV33,col.name = "HPV33.reads")
FeaturePlot(acc1_cancer_cells,features = "HPV33.reads",max.cutoff = 40)

data = FetchData(object = acc1_cancer_cells,vars = "HPV33.reads")
print(
  data %>% 
  ggplot(aes( x=HPV33.reads)) + 
  geom_density()
)
hpv33_positive = HPV33 %>% dplyr::mutate(hpv33_positive = if_else(reads > 20,true =  "positive",false =  "negative"))
hpv33_positive$reads = NULL
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = hpv33_positive)

cNMF

library(reticulate)
#write expression
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 2000)
vargenes = VariableFeatures(object = acc1_cancer_cells)
hmsc_expression = t(as.matrix(GetAssayData(acc1_cancer_cells,slot='data')))
hmsc_expression = 2**hmsc_expression #convert from log2(tpm+1) to tpm
hmsc_expression = hmsc_expression-1
# hmsc_expression = hmsc_expression[,!colSums(hmsc_expression==0, na.rm=TRUE)==nrow(hmsc_expression)] #delete rows that have all 0
hmsc_expression = hmsc_expression[,vargenes]
write.table(x = hmsc_expression ,file = './Data/cNMF/hmsc_expressionData_2Kvargenes.txt',sep = "\t")
from cnmf import cNMF
name = 'HMSC_cNMF_2Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/hmsc_expressionData_2Kvargenes.txt'
tpm_fn = counts_fn ## This is a weird case where because this dataset is not 3' end umi sequencing, we opted to use the TPM matrix as the input matrix rather than the count matrix

cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, seed=14,tpm_fn=tpm_fn)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()

Save object

import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'wb')
pickle.dump(cnmf_obj, f)
f.close()

Load object

from cnmf import cNMF
import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
  view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
# calculate usage by expression*genes coefs:
gep_scores = py$gep_scores
all_metagenes= py$usage_norm

Enrichment analysis by top 200 genes of each program

plt_list = list()
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title = 
                    i,silent = T,return_all = T)
   
  plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)

# Make metagene names
for (i in 1:ncol(all_metagenes)) {
  colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}

#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
  metage_metadata = all_metagenes %>% select(i)
  acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}

FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes))

assignment UMAP

larger_by = 2
message(paste("larger_by = ", larger_by))
larger_by =  2
acc1_cancer_cells = program_assignment(dataset = acc1_cancer_cells,larger_by = larger_by,program_names = colnames(all_metagenes))
selected_k = py$selected_k
colors =  rainbow(selected_k)
colors = c(colors,"grey")
DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols =colors)

Comparisions

cnv_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","hpv33_positive"))
test <- fisher.test(table(cnv_vs_hpv))
ggbarstats(
  cnv_vs_hpv, cnv.cluster, hpv33_positive,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)

cnv_vs_cnmf = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","cnv.cluster"))
# cnv_vs_hpv =cnv_vs_hpv %>%  mutate(program.assignment = if_else(condition = program.assignment == "1",true = "1",false = "not 1"))
cnv_vs_cnmf = cnv_vs_cnmf %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnv_vs_cnmf))
ggbarstats(
  cnv_vs_cnmf, program.assignment, cnv.cluster,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)

cnmf_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","hpv33_positive"))
cnmf_vs_hpv = cnmf_vs_hpv %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnmf_vs_hpv))
ggbarstats(
  cnmf_vs_hpv, program.assignment, hpv33_positive,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)

myb_vs_cnv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","MYB"))
myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )

ggboxplot(myb_vs_cnv, x = "cnv.cluster", y = "MYB",
          palette = "jco",
          add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("1","2")))

myb_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("hpv33_positive","MYB"))
myb_vs_hpv $hpv33_positive = as.character(myb_vs_hpv $hpv33_positive )

ggboxplot(myb_vs_hpv, x = "hpv33_positive", y = "MYB",
          palette = "jco",
          add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("positive","negative")))

hpvReads_vs_myb = FetchData(object = acc1_cancer_cells,vars = c("HPV33.reads","MYB"))
corr = cor(hpvReads_vs_myb$HPV33.reads,hpvReads_vs_myb$MYB)
print("correlation of MYB abd hpv33_reads:" %>% paste(corr %>% round(digits = 2)) )
[1] "correlation of MYB abd hpv33_reads: 0.21"
LS0tCnRpdGxlOiAiVGl0bGUiCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KYGBge3IsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBpbmNsdWRlID0gVAopCmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUygiLi9EYXRhL2FjYzFfY2FuY2VyX2NlbGxzXzE1S25Db3VudF9WMy5SRFMiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoIi4vRGF0YS9hY2NfY2FuY2VyX2NlbGxzX1YzLlJEUyIpCmFjY19hbGxfY2VsbHMgPSByZWFkUkRTKCIuL0RhdGEvYWNjX3RwbV9uQ291bnRfbWl0b19ubzE0Nl8xNWtfd2l0aF9BQ0MxXy5SRFMiKQpsdW1pbmFsX3BhdGh3YXlzID0gYygiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfQkFTQUxfRE4iLCJDSEFSQUZFX0JSRUFTVF9DQU5DRVJfTFVNSU5BTF9WU19CQVNBTF9VUCIsIkNIQVJBRkVfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX1ZTX01FU0VOQ0hZTUFMX0ROIiwiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfTUVTRU5DSFlNQUxfVVAiLCJIVVBFUl9CUkVBU1RfQkFTQUxfVlNfTFVNSU5BTF9ETiIsIkxJTV9NQU1NQVJZX0xVTUlOQUxfUFJPR0VOSVRPUl9VUCIsIlNNSURfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX0JfVVAiICkKCiMgYWRkIGx1bWluYWwgcGF0aHdheXMKbHVtaW5hbF9ncyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKSAlPiVhcy5kYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSAlaW4lIGx1bWluYWxfcGF0aHdheXMpJT4lIGRwbHlyOjpkaXN0aW5jdChnc19uYW1lLCBnZW5lX3N5bWJvbCkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpzdWZmaXggPSAiMDFfMjUiCmRhdGFfdG9fcmVhZCA9ICIiCmBgYAoKCiMjIGZ1bmN0aW9ucwoKYGBge3Igd2FybmluZz1GQUxTRX0Kc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJERUdfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMi4yNCIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiSE1TQ19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4xLjExIixzY3JpcHRfbmFtZSA9ICJmdW5jdGlvbnMuUiIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiY05NRl9mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4zLjUzIixzY3JpcHRfbmFtZSA9ICJjbm1mX2Z1bmN0aW9uX0hhcm1vbnkuUiIpCmBgYAoKCiMjIEVucmljaG1lbnQgYW5hbHlzaXMgSE1TQyB2cyBBQ0MKYGBge3IgZmlnLndpZHRoPTgsIGVjaG89VFJVRSxyZXN1bHRzPSdoaWRlJyxmaWcua2VlcD0nYWxsJ30KcGF0aWVudC5pZGVudCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzJHBhdGllbnQuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpwYXRpZW50LmlkZW50WywxXSA9IGFzLmNoYXJhY3RlcihwYXRpZW50LmlkZW50WywxXSkKcGF0aWVudC5pZGVudFtwYXRpZW50LmlkZW50WywxXSA9PSAiQUNDMSIsXSA9ICJITVNDIgpwYXRpZW50LmlkZW50WywxXSA9IGFzLmZhY3RvcihwYXRpZW50LmlkZW50WywxXSkKYWxsX2FjY19jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IHBhdGllbnQuaWRlbnQsY29sLm5hbWUgPSAicGF0aWVudC5pZGVudCIpCmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQoYWxsX2FjY19jYW5jZXJfY2VsbHMsIHZhbHVlID0icGF0aWVudC5pZGVudCIpCmFjY19kZWcgPC0gRmluZE1hcmtlcnMoYWxsX2FjY19jYW5jZXJfY2VsbHMsIGlkZW50LjEgPSAiSE1TQyIsbG9nZmMudGhyZXNob2xkID0gMS41LGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxscykpCmVucmljaG1lbnRfYW5hbHlzaXMoYWNjX2RlZyxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxscyksZmRyX0N1dG9mZiA9IDAuMDEsaWRlbnQuMSA9ICJITVNDIixpZGVudC4yID0gIkFDQyIsc2hvd19ieSA9IDEpCmBgYAoKIyMgY2VsbCBjeWNsZSBmaWx0ZXJpbmcgey50YWJzZXR9CgoKYGBge3Igd2FybmluZz1GQUxTRX0KaGFsbG1hcmtfbmFtZSA9ICJHT19NSVRPVElDX0NFTExfQ1lDTEUiCmdlbmVzZXRzICA9Z2V0R210KCIuL0RhdGEvaC5hbGwudjcuMC5zeW1ib2xzLnBsdXNjYy5nbXQiKQp2YXJfZmVhdHVyZXM9YWxsX2FjY19jYW5jZXJfY2VsbHNAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMKZ2VuZUlkcz0gZ2VuZXNldHNbW2hhbGxtYXJrX25hbWVdXUBnZW5lSWRzCnNjb3JlIDwtIGFwcGx5KGFsbF9hY2NfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVtpbnRlcnNlY3QoZ2VuZUlkcyx2YXJfZmVhdHVyZXMpLF0sMixtZWFuKQphbGxfYWNjX2NhbmNlcl9jZWxscz1BZGRNZXRhRGF0YShhbGxfYWNjX2NhbmNlcl9jZWxscyxzY29yZSxoYWxsbWFya19uYW1lKQoKI2ZpbHRlcjoKYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZD1hbGxfYWNjX2NhbmNlcl9jZWxsc1ssYWxsX2FjY19jYW5jZXJfY2VsbHNAbWV0YS5kYXRhW1toYWxsbWFya19uYW1lXV08IDAuM10KCgptaW5fdGhyZXNob2xkID0gbWluKGFsbF9hY2NfY2FuY2VyX2NlbGxzJEdPX01JVE9USUNfQ0VMTF9DWUNMRSkKbWF4X3RocmVzaG9sZCA9IG1heChhbGxfYWNjX2NhbmNlcl9jZWxscyRHT19NSVRPVElDX0NFTExfQ1lDTEUpCmBgYAoKCiMjIyBCZWZvcmUgY2MgZmlsdGVyaW5nCgoKYGBge3J9CkZlYXR1cmVQbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gaGFsbG1hcmtfbmFtZSkgKyBnZ3RpdGxlKCJCZWZvcmUgY2MgZmlsdGVyaW5nIikgICYgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSBwbGFzbWEobiA9IDEwLCBkaXJlY3Rpb24gPSAtMSksIGxpbWl0cyA9IGMobWluX3RocmVzaG9sZCwgbWF4X3RocmVzaG9sZCkpCmBgYAoKIyMjIEFmdGVyIGNjIGZpbHRlcmluZwoKYGBge3J9CkZlYXR1cmVQbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsZmVhdHVyZXMgPSBoYWxsbWFya19uYW1lKSArIGdndGl0bGUoIkFmdGVyIGNjIGZpbHRlcmluZyIpICYgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSBwbGFzbWEobiA9IDEwLCBkaXJlY3Rpb24gPSAtMSksIGxpbWl0cyA9IGMobWluX3RocmVzaG9sZCwgbWF4X3RocmVzaG9sZCkpCmBgYAojIyB7LX0KCiMjIEVucmljaG1lbnQgYW5hbHlzaXMgZmlsdGVyZWQgSE1TQyB2cyBBQ0MKYGBge3IgZmlnLndpZHRoPTgsIGVjaG89VFJVRSxyZXN1bHRzPSdoaWRlJyxmaWcua2VlcD0nYWxsJ30KcGF0aWVudC5pZGVudCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQkcGF0aWVudC5pZGVudCAlPiUgYXMuZGF0YS5mcmFtZSgpCnBhdGllbnQuaWRlbnRbLDFdID0gYXMuY2hhcmFjdGVyKHBhdGllbnQuaWRlbnRbLDFdKQpwYXRpZW50LmlkZW50W3BhdGllbnQuaWRlbnRbLDFdID09ICJBQ0MxIixdID0gIkhNU0MiCnBhdGllbnQuaWRlbnRbLDFdID0gYXMuZmFjdG9yKHBhdGllbnQuaWRlbnRbLDFdKQphbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCxtZXRhZGF0YSA9IHBhdGllbnQuaWRlbnQsY29sLm5hbWUgPSAicGF0aWVudC5pZGVudCIpCmFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQgPSBTZXRJZGVudChhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLCB2YWx1ZSA9InBhdGllbnQuaWRlbnQiKQphY2NfZGVnIDwtIEZpbmRNYXJrZXJzKGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsIGlkZW50LjEgPSAiSE1TQyIsbG9nZmMudGhyZXNob2xkID0gMS41LGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkKSkKZW5yaWNobWVudF9hbmFseXNpcyhhY2NfZGVnLGJhY2tncm91bmQgPSBWYXJpYWJsZUZlYXR1cmVzKGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQpLGZkcl9DdXRvZmYgPSAwLjAxLGlkZW50LjEgPSAiSE1TQyIsaWRlbnQuMiA9ICJBQ0MiLHNob3dfYnkgPSAxKQpgYGAKCiMjIE1ZQiBleHByZXNzaW9uCgpgYGB7ciBmaWcud2lkdGg9MTB9CmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHMsdmFsdWUgPSAicGF0aWVudC5pZGVudCIpICNhY3RpdmUgc25uIGdyYXBoCkZlYXR1cmVQbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gIk1ZQiIsbGFiZWwgPSBUKQpgYGAKCiMjIENOViB7LnRhYnNldH0KCgoKYGBge3J9CiNzZXQgY2VsbCB0eXBlcwpuZXcuY2x1c3Rlci5pZHMgPC0gYygiY2FuY2VyIiwgIzAKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxCiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjMgogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzMKICAgICAgICAgICAgICAgICAgICAgIkVuZG90aGVsaWFsIiwgIzQKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICM1CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjNgogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzcKICAgICAgICAgICAgICAgICAgICAgIkNBRiIsICM4CiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjOQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzEwCiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjMTEKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMgogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzEzCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTQKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxNQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzE2CiAgICAgICAgICAgICAgICAgICAgICJXQkMiLCAjMTcKICAgICAgICAgICAgICAgICAgICAgIkNBRiIgIzE4CiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgoKYGBge3IgZmlnLnNob3c9J2hpZGUnfQojcmVuYW1lIGlkZW50czoKYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAiUk5BX3Nubl9yZXMuMSIpICNhY3RpdmUgc25uIGdyYXBoCm5hbWVzKG5ldy5jbHVzdGVyLmlkcykgPC0gbGV2ZWxzKGFjY19hbGxfY2VsbHMpICNhZGQgc25uIGdyYXBoIGxldmVscyB0byBuZXcuY2x1c3Rlci5pZHMKYWNjX2FsbF9jZWxsc0BtZXRhLmRhdGFbWyJzZXVyYXRfY2x1c3RlcnMiXV0gPSBhY2NfYWxsX2NlbGxzQG1ldGEuZGF0YVtbIlJOQV9zbm5fcmVzLjEiXV0KYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAic2V1cmF0X2NsdXN0ZXJzIikKYWNjX2FsbF9jZWxscyA8LSBSZW5hbWVJZGVudHMoYWNjX2FsbF9jZWxscywgbmV3LmNsdXN0ZXIuaWRzKSAKCiMgZGl2aWRlICJjYW5jZXIiIGludG8gcGF0aWVudHM6CmNlbGxfdHlwZXMgPSBhY2NfYWxsX2NlbGxzQGFjdGl2ZS5pZGVudCAlPiUgYXMuZGF0YS5mcmFtZSgpCmNlbGxfdHlwZXNbLDFdPC0gYXMuY2hhcmFjdGVyKGNlbGxfdHlwZXNbLDFdKQpjZWxsX3R5cGVzID0gY2JpbmQoY2VsbF90eXBlcyxhY2NfYWxsX2NlbGxzJHBhdGllbnQuaWRlbnQpICU+JSBzZXRuYW1lcyhvbGQgPSBuYW1lcyguKSwgCiAgICAgICAgIG5ldyA9IGMoJ2NlbGxfdHlwZScsJ3BhdGllbnQnKSkKY2VsbF90eXBlc1tjZWxsX3R5cGVzJGNlbGxfdHlwZSA9PSAiY2FuY2VyIixdID0gY2VsbF90eXBlc1tjZWxsX3R5cGVzJGNlbGxfdHlwZSA9PSAiY2FuY2VyIiwyXQoKCiMgaG1zY19yb3dzID0gKHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MucGxhdGUyIikgfCBzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDMS4iKSkgJiBjZWxsX3R5cGVzWywxXSA9PSAiY2FuY2VyIiAKIyBhY2Nfcm93cyA9ICEoc3RhcnRzV2l0aCh4ID0gcm93bmFtZXMoY2VsbF90eXBlcykscHJlZml4ID0gIkFDQy5wbGF0ZTIiKSB8IHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MxLiIpKSAmIGNlbGxfdHlwZXNbLDFdID09ICJjYW5jZXIiIAojIGNlbGxfdHlwZXNbLDFdW2htc2Nfcm93c10gID0gIkhNU0MiCiMgY2VsbF90eXBlc1ssMV1bYWNjX3Jvd3NdICA9ICJBQ0MiCgojYWRkIHRvIG1ldGFkYXRhOgpjZWxsX3R5cGVzWywyXSA9IE5VTEwgCmNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gIkFDQzEiLF0gPSAiSE1TQyIKYWNjX2FsbF9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9YWNjX2FsbF9jZWxscyAsbWV0YWRhdGEgPSBjZWxsX3R5cGVzLGNvbC5uYW1lID0gImNlbGwudHlwZSIpCmBgYAojIyMgQ05WIFVNQVAgCgpgYGB7ciBmaWcud2lkdGg9MTB9CmxpYnJhcnkoaW5mZXJjbnYpCmxpYnJhcnkodGlkeXZlcnNlKQphY2NfYW5ub3RhdGlvbiAgPSBhcy5kYXRhLmZyYW1lKGFjY19hbGxfY2VsbHNAbWV0YS5kYXRhWywiY2VsbC50eXBlIixkcm9wID0gRl0pCmFjY19hbm5vdGF0aW9uID0gYWNjX2Fubm90YXRpb24gJT4lIHJvd25hbWVzX3RvX2NvbHVtbigib3JpZy5pZGVudCIpIAphY2NfYW5ub3RhdGlvbiA9IGFjY19hbm5vdGF0aW9uICU+JSBtdXRhdGUob3JpZy5pZGVudCA9IGdzdWIoeCA9IGFjY19hbm5vdGF0aW9uJG9yaWcuaWRlbnQscGF0dGVybiA9ICJcXC4iLCByZXBsYWNlbWVudCA9ICItIikgJT4lIAogIGdzdWIocGF0dGVybiA9ICJfIiwgcmVwbGFjZW1lbnQgPSAiLSIpKQogIAoKd3JpdGUudGFibGUoYWNjX2Fubm90YXRpb24sICIuL0RhdGEvaW5mZXJDTlYvYWNjX2Fubm90YXRpb24udHh0IiwgYXBwZW5kID0gRkFMU0UsIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBkZWMgPSAiLiIscm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEYpCgppbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0iLi9EYXRhL2luZmVyQ05WL2FsbC40aWNudi50eHQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbnNfZmlsZT0iLi9EYXRhL2luZmVyQ05WL2FjY19hbm5vdGF0aW9uLnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbGltPSJcdCIsZ2VuZV9vcmRlcl9maWxlPSIuL0RhdGEvaW5mZXJDTlYvZ2VuY29kZV92MTlfZ2VuZV9wb3MudHh0IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAscmVmX2dyb3VwX25hbWVzPWMoIkNBRiIsICJFbmRvdGhlbGlhbCIsICJXQkMiKSkgI2dyb3VwcyBvZiBub3JtYWwgY2VsbHMKCmluZmVyY252X29ial9kZWZhdWx0ID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosIGN1dG9mZj0xLCBvdXRfZGlyPScuL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnZfb3V0cHV0JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzPVQsIHBsb3Rfc3RlcHM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVRSVUUsIEhNTT1GQUxTRSwgbm9fcHJlbGltX3Bsb3Q9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBuZ19yZXM9MzAwKQpwbG90X2NudihpbmZlcmNudl9vYmpfZGVmYXVsdCwgb3V0cHV0X2Zvcm1hdCA9ICJwbmciLCAgd3JpdGVfZXhwcl9tYXRyaXggPSBGQUxTRSxvdXRfZGlyID0gIi4vRGF0YS9pbmZlckNOVi8iLHBuZ19yZXMJPTgwMCxvYnNfdGl0bGUgPSAiTWFsaWduYW50IGNlbGxzIixyZWZfdGl0bGUgPSAiTm9ybWFsIGNlbGxzIikKCgpjbHVzdGVyLmluZm89RmV0Y2hEYXRhKGFjY19hbGxfY2VsbHMsYygiaWRlbnQiLCJvcmlnLmlkZW50IiwiVU1BUF8xIiwiVU1BUF8yIiwibkNvdW50X1JOQSIsIm5GZWF0dXJlX1JOQSIsInBlcmNlbnQubXQiLCJwYXRpZW50LmlkZW50Iiwic2V1cmF0X2NsdXN0ZXJzIikpCmNsdXN0ZXIuaW5mbyRjZWxsPXJvd25hbWVzKGNsdXN0ZXIuaW5mbykKCmxpYnJhcnkobGltbWEpCnNtb290aGVkPWFwcGx5KGluZmVyY252X29ial9kZWZhdWx0QGV4cHIuZGF0YSwyLHRyaWN1YmVNb3ZpbmdBdmVyYWdlLCBzcGFuPTAuMDEpCmNuc2lnPXNxcnQoYXBwbHkoKHNtb290aGVkLTEpXjIsMixtZWFuKSkKdW1hcD1jbHVzdGVyLmluZm8KbmFtZXMoY25zaWcpPXVtYXAkY2VsbAoKYWNjX2FsbF9jZWxscyA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2NfYWxsX2NlbGxzLCBtZXRhZGF0YSA9IGNuc2lnLCBjb2wubmFtZSA9ICJjb3B5bnVtYmVyIikKYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAiY2VsbC50eXBlIikKRmVhdHVyZVBsb3QoYWNjX2FsbF9jZWxscywgImNvcHludW1iZXIiLHB0LnNpemUgPSAxLCBjb2xzID0gYygibGlnaHRibHVlIiwib3JhbmdlIiwicmVkIiwiZGFya3JlZCIpLGxhYmVsID0gVCxyZXBlbCA9IFQpCmBgYAoKCiMjIyBDTlYgcGxvdCAKCiFbQ05WIHBsb3RdKC9zY2kvbGFicy95b3RhbWQvbGFiX3NoYXJlL2F2aXNoYWkud2l6ZWwvUl9wcm9qZWN0cy9ITVNDL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnYucG5nKQojIyB7LX0KCiMjIENOViBzdWJ0eXBlcwoKYGBge3J9CmNudl9zdWJ0eXBlcyA9IGFzLmRhdGEuZnJhbWUoY3V0cmVlKGluZmVyY252X29ial9kZWZhdWx0QHR1bW9yX3N1YmNsdXN0ZXJzW1siaGMiXV1bWyJITVNDIl1dLCBrID0gMikpCm5hbWVzKGNudl9zdWJ0eXBlcylbMV0gPSAiY252LmNsdXN0ZXIiCnJvd25hbWVzKGNudl9zdWJ0eXBlcykgPSByb3duYW1lcyhjbnZfc3VidHlwZXMpICU+JSBnc3ViKHBhdHRlcm4gPSAiLSIscmVwbGFjZW1lbnQgPSAiXFwuIikKaW5mZXJjbnYub2JzZXJ2YXRpb25zID0gZGF0YS5mcmFtZShmcmVhZChmaWxlID0gIi4vRGF0YS9pbmZlckNOVi9pbmZlcmNudi5vYnNlcnZhdGlvbnMudHh0IiksIHJvdy5uYW1lcz0xKQpuYW1lc190b19rZWVwID0gY29sbmFtZXMoaW5mZXJjbnYub2JzZXJ2YXRpb25zKSAlaW4lIChjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykgJT4lIGdzdWIocGF0dGVybiA9ICJfIixyZXBsYWNlbWVudCA9ICJcXC4iKSkKaW5mZXJjbnYub2JzZXJ2YXRpb25zID0gaW5mZXJjbnYub2JzZXJ2YXRpb25zWyxuYW1lc190b19rZWVwXQpgYGAKCmBgYHtyfQpyb3RhdGUgPC0gZnVuY3Rpb24oeCkgdChhcHBseSh4LCAyLCByZXYpKQppbmZlcmNudi5vYnNlcnZhdGlvbnMyID0gaW5mZXJjbnYub2JzZXJ2YXRpb25zICU+JSByb3RhdGUoKSAlPiUgIHJvdGF0ZSgpICU+JSByb3RhdGUoKSU+JSBhcy5kYXRhLmZyYW1lKCkgCmJyZWFrcyA9IGMoMC43MDA4OTE4NjE3MDQ4NTcsCjAuNzQyMzY2OTQ1NTI4MzY5LAowLjc4Mzg0MjAyOTM1MTg4MSwKMC44MjUzMTcxMTMxNzUzOTMsCjAuODY2NzkyMTk2OTk4OTA1LAowLjkwODI2NzI4MDgyMjQxNywKMC45NDk3NDIzNjQ2NDU5MjgsCjAuOTkxMjE3NDQ4NDY5NDQsCjEuMDMyNjkyNTMyMjkyOTUsCjEuMDc0MTY3NjE2MTE2NDYsCjEuMTE1NjQyNjk5OTM5OTgsCjEuMTU3MTE3NzgzNzYzNDksCjEuMTk4NTkyODY3NTg3LAoxLjI0MDA2Nzk1MTQxMDUxLAoxLjI4MTU0MzAzNTIzNDAyLAoxLjMyMzAxODExOTA1NzUzKQpwaGVhdG1hcChpbmZlcmNudi5vYnNlcnZhdGlvbnMyLGNsdXN0ZXJfY29scyA9IEYsY2x1c3Rlcl9yb3dzID0gRiwgc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IEYsIGJyZWFrcyA9IGJyZWFrcyxjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUocmV2KGMoImRhcmtyZWQiLCAid2hpdGUiLCAiZGFya2JsdWUiKSkpKDE1KSxhbm5vdGF0aW9uX3JvdyA9IGNudl9zdWJ0eXBlcykKCmBgYAoKYGBge3J9CnJvd25hbWVzKGNudl9zdWJ0eXBlcykgPSByb3duYW1lcyhjbnZfc3VidHlwZXMpICU+JSBnc3ViKHBhdHRlcm4gPSAiMlxcLiIscmVwbGFjZW1lbnQgPSAiMl8iKQpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIjNcXC4iLHJlcGxhY2VtZW50ID0gIjNfIikKYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IGNudl9zdWJ0eXBlcykKYGBgCgpgYGB7cn0KRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxncm91cC5ieSA9ICJjbnYuY2x1c3RlciIscHQuc2l6ZSA9IDIsY29scyA9Y29sb3JzKQpgYGAKCgojIyBPcmlnaW5hbCBzY29yZQpgYGB7cn0Kb3JpZ2luYWxfbXlvX2dlbmVzID0gYyggIlRQNjMiLCAiVFA3MyIsICJDQVYxIiwgIkNESDMiLCAiS1JUNSIsICJLUlQxNCIsICJBQ1RBMiIsICJUQUdMTiIsICJNWUxLIiwgIkRLSzMiKQpvcmlnaW5hbF9sdW1fZ2VuZXMgPSBjKCJLSVQiLCAiRUhGIiwgIkVMRjUiLCAiS1JUNyIsICJDTEROMyIsICJDTERONCIsICJDRDI0IiwgIkxHQUxTMyIsICJMQ04yIiwgIlNMUEkiICkKYGBgCgoKCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMpCmBgYAojIyBPcmlnaW5hbCBzY29yZSBvZiBBQ0MxCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDAsbXlvX3RocmVzaG9sZCA9IDApCmBgYAoKCiMjIDAuMzUgTW9zdCBjb3JyZWxhdGVkIHNjb3JlIHsudGFic2V0fQoKIyMjIE15byBnZW5lcwoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGNvbGxhcHNlPVR9Cm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4zNSkKcHJpbnQoIk51bWJlciBvZiBnZW5lcyA9ICIgJT4lIHBhc3RlKGxlbmd0aCh0b3BfbXlvKSkpCm1lc3NhZ2UoIk5hbWVzIG9mIGdlbmVzOiIpCnRvcF9teW8gJT4lIGhlYWQoMzApCm1lc3NhZ2UoIkdlbmVzIHRoYXQgYWxzbyBhcGVhcmVkIGluIHRoZSBvcmlnaW5hbCBzY29yZToiKQpiYXNlOjppbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApgYGAKYGBge3J9Cm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gIm15byB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbXlvX2VucmljaF9yZXMKYGBgCiMjIyBMdW0gZ2VuZXMKYGBge3J9Cmx1bV9wcm90ZWluX21hcmtlcnMgPSBjKCJLSVQiKQp0b3BfbHVtICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBsdW1fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMzUpCnByaW50KCJOdW1iZXIgb2YgZ2VuZXMgPSAiICU+JSBwYXN0ZShsZW5ndGgodG9wX2x1bSkpKQptZXNzYWdlKCJOYW1lcyBvZiBnZW5lczoiKQp0b3BfbHVtICU+JSBoZWFkKDMwKQptZXNzYWdlKCJHZW5lcyB0aGF0IGFsc28gYXBlYXJlZCBpbiB0aGUgb3JpZ2luYWwgc2NvcmU6IikKYmFzZTo6aW50ZXJzZWN0KHRvcF9sdW0sb3JpZ2luYWxfbHVtX2dlbmVzKSAKYGBgCgpgYGB7cn0KbHVtX2VucmljaF9yZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcF9sdW0sYmFja2dyb3VuZCA9IHJvd25hbWVzKGFjYzFfY2FuY2VyX2NlbGxzKSxob21lciA9IFQsdGl0bGUgPSAibHVtIHRvcCBlbnJpY2htZW50IixjdXN0b21fcGF0aHdheXMgPSBsdW1pbmFsX2dzKQpsdW1fZW5yaWNoX3JlcwpgYGAKIyMjIHRvcCBjb3JyZWxhdGVkIHNjb3JlCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IHRvcF9teW8sbHVtX2dlbmVzID0gdG9wX2x1bSxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gMCkKYGBgCgoKIyMjICBlbnJpY2hlZCBnZW5lcyBzY29yZQpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzEsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMSwiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9CmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gLTIuNSxteW9fdGhyZXNob2xkID0gLTIuNSkKYGBgCgoKIyMgey19CgoKIyMgIDAuMiBNb3N0IGNvcnJlbGF0ZWQgc2NvcmUgey50YWJzZXR9CgojIyMgIG15byBHZW5lcwoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4yKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9teW8pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX215byAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbXlvLG9yaWdpbmFsX215b19nZW5lcykgCmBgYAoKYGBge3J9Cm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gIm15byB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbXlvX2VucmljaF9yZXMKYGBgCiMjIyAgTHVtIEdlbmVzCgpgYGB7cn0KbHVtX3Byb3RlaW5fbWFya2VycyA9IGMoIktJVCIpCnRvcF9sdW0gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IGx1bV9wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4yKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9sdW0pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX2x1bSAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbHVtLG9yaWdpbmFsX2x1bV9nZW5lcykgCmBgYAoKYGBge3J9Cmx1bV9lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbHVtLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gImx1bSB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbHVtX2VucmljaF9yZXMKYGBgCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUgPSBmdW5jdGlvbihkYXRhc2V0LG15b19nZW5lcyxsdW1fZ2VuZXMsbHVtX3RocmVzaG9sZCA9MSAsIG15b190aHJlc2hvbGQgPSAtMSkgewogIG15b3Njb3JlPWFwcGx5KGRhdGFzZXRAYXNzYXlzW1siUk5BIl1dW215b19nZW5lcyxdLDIsbWVhbikKICBsZXNjb3JlPWFwcGx5KGRhdGFzZXRAYXNzYXlzW1siUk5BIl1dW2x1bV9nZW5lcyxdLDIsbWVhbikKICBjb3JyZWxhdGlvbiA9IGNvcihsZXNjb3JlLG15b3Njb3JlKSAlPiUgcm91bmQoZGlnaXRzID0gMikKICBtZXNzYWdlKCJjb3JyZWxhdGlvbiBvZiBsdW0gc2NvcmUgYW5kIG15byBzY29yZToiICU+JSBwYXN0ZShjb3JyZWxhdGlvbikpCiAgCiAgCiAgb3JpZ2luYWxfbXlvX2dlbmVzID0gYygiVFA2MyIsICJUUDczIiwgIkNBVjEiLCAiQ0RIMyIsICJLUlQ1IiwgIktSVDE0IiwgIkFDVEEyIiwgIlRBR0xOIiwgIk1ZTEsiLCAiREtLMyIpCiAgb3JpZ2luYWxfbHVtX2dlbmVzID0gYygiS0lUIiwgIkVIRiIsICJFTEY1IiwgIktSVDciLCAiQ0xETjMiLCAiQ0xETjQiLCAiQ0QyNCIsICJMR0FMUzMiLCAiTENOMiIsICJTTFBJIikKICBvcmlnX215b3Njb3JlPWFwcGx5KGRhdGFzZXRAYXNzYXlzW1siUk5BIl1dW29yaWdpbmFsX215b19nZW5lcyxdLDIsbWVhbikKICBvcmlnX2xlc2NvcmU9YXBwbHkoZGF0YXNldEBhc3NheXNbWyJSTkEiXV1bb3JpZ2luYWxfbHVtX2dlbmVzLF0sMixtZWFuKQogIGNvcnJlbGF0aW9uX3RvX29yaWdpbmFsX2x1bSA9IGNvcihvcmlnX2xlc2NvcmUsbGVzY29yZSkgJT4lIHJvdW5kKGRpZ2l0cyA9IDIpCiAgY29ycmVsYXRpb25fdG9fb3JpZ2luYWxfbXlvID0gY29yKG9yaWdfbXlvc2NvcmUsbXlvc2NvcmUpICU+JSByb3VuZChkaWdpdHMgPSAyKQoKICBtZXNzYWdlKCJjb3JyZWxhdGlvbiBvZiBsdW0gc2NvcmUgYW5kIG9yaWdpbmFsIGx1bSBzY29yZToiICU+JSBwYXN0ZShjb3JyZWxhdGlvbl90b19vcmlnaW5hbF9sdW0pKQogIG1lc3NhZ2UoImNvcnJlbGF0aW9uIG9mIG15byBzY29yZSBhbmQgb3JpZ2luYWwgbXlvIHNjb3JlOiIgJT4lIHBhc3RlKGNvcnJlbGF0aW9uX3RvX29yaWdpbmFsX215bykpCgogIGRhdGFzZXQ9QWRkTWV0YURhdGEoZGF0YXNldCxsZXNjb3JlLW15b3Njb3JlLCJsdW1pbmFsX292ZXJfbXlvIikKICBwcmludCgKICAgIEZlYXR1cmVQbG90KG9iamVjdCA9IGRhdGFzZXQsZmVhdHVyZXMgPSAibHVtaW5hbF9vdmVyX215byIpCiAgKQogIGRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gZGF0YXNldCx2YXJzID0gImx1bWluYWxfb3Zlcl9teW8iKQogIHByaW50KAogICAgZGF0YSAlPiUgCiAgICBnZ3Bsb3QoYWVzKCB4PWx1bWluYWxfb3Zlcl9teW8pKSArIAogICAgZ2VvbV9kZW5zaXR5KCkgCiAgICApCiAgCmx1bV9jZWxsc19udW0gPSBzdWJzZXQoeCA9IGRhdGFzZXQsbHVtaW5hbF9vdmVyX215byA+KGx1bV90aHJlc2hvbGQpKSAlPiUgbmNvbCgpIC9uY29sKGRhdGFzZXQpCm15b19jZWxsc19udW0gPSBzdWJzZXQoeCA9IGRhdGFzZXQsbHVtaW5hbF9vdmVyX215byA8KG15b190aHJlc2hvbGQpKSAlPiUgbmNvbCgpL25jb2woZGF0YXNldCkKZGYgPSBkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IGMoIm15b19jZWxscyIsImx1bV9jZWxscyIpLHBlcmNlbnRhZ2UgPSBjKG15b19jZWxsc19udW0sbHVtX2NlbGxzX251bSkpCmdncGxvdChkYXRhPWRmLCBhZXMoeD1jZWxsX3R5cGUsIHk9cGVyY2VudGFnZSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgZ2d0aXRsZSgiQUNDIGNlbGwgdHlwZXMiKQp9CgpgYGAKCmBgYHtyfQpteW9faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApsdW1faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX2x1bSxvcmlnaW5hbF9sdW1fZ2VuZXMpIAptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2ludGVyc2VjdGVkCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2ludGVyc2VjdGVkCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2ludGVyc2VjdGVkLGx1bV9nZW5lcyA9IGx1bV9pbnRlcnNlY3RlZCxsdW1fdGhyZXNob2xkID0gMixteW9fdGhyZXNob2xkID0gMSkKYGBgCgoKCiMjIyBlbnJpY2hlZCBnZW5lcwpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzMsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMywiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9Cm1lc3NhZ2UoImdlbmVzIGluIG15byBzY29yZToiKQpteW9fZW5yaWNoZWRfZ2VuZXMKCm1lc3NhZ2UoImdlbmVzIGluIGx1bSBzY29yZToiKQpsdW1fZW5yaWNoZWRfZ2VuZXMKCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gLTEpCmBgYAoKCiMjIyBlbnJpY2hlZCBnZW5lcyBhbmQgaW4gb3JpZ2luYWwgc2NvcmUKYGBge3J9Cm15b19lbnJpY2hlZF9nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lc1tteW9fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9teW9fZ2VuZXNdCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lc1tsdW1fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9sdW1fZ2VuZXNdCgptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2VucmljaGVkX2dlbmVzCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2VucmljaGVkX2dlbmVzCgpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lcyxsdW1fZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDIpCmBgYAoKIyMgey19CgoKIyBIUFYKCk9ubHkgSE1TQyBjYW5jZXIgY2VsbHM6CgpgYGB7cn0KSFBWMzNfUDMgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AzLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDMkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICItUCIscmVwbGFjZW1lbnQgPSAiLlAiKSAKICAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIl8iLCkKICApCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDMuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AzLmRmKSAgPC0gSFBWMzNfUDMuZGYkcGxhdGUKSFBWMzNfUDMuZGYkcGxhdGUgPSBOVUxMCgoKSFBWMzNfUDIgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AyLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDIkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICJwbGF0ZTItIixyZXBsYWNlbWVudCA9ICJwbGF0ZTJfIiwpCiAgJT4lIGdzdWIocGF0dGVybiA9ICItIixyZXBsYWNlbWVudCA9ICJcXC4iLCkKICApCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDIuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AyLmRmKSAgPC0gSFBWMzNfUDIuZGYkcGxhdGUKSFBWMzNfUDIuZGYkcGxhdGUgPSBOVUxMCgpIUFYzMyA9IHJiaW5kKEhQVjMzX1AzLmRmLEhQVjMzX1AyLmRmKQphY2MxX2NhbmNlcl9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG1ldGFkYXRhID0gSFBWMzMsY29sLm5hbWUgPSAiSFBWMzMucmVhZHMiKQpGZWF0dXJlUGxvdChhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJIUFYzMy5yZWFkcyIsbWF4LmN1dG9mZiA9IDQwKQpgYGAKYGBge3J9CmRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9ICJIUFYzMy5yZWFkcyIpCnByaW50KAogIGRhdGEgJT4lIAogIGdncGxvdChhZXMoIHg9SFBWMzMucmVhZHMpKSArIAogIGdlb21fZGVuc2l0eSgpCikKYGBgCmBgYHtyfQpocHYzM19wb3NpdGl2ZSA9IEhQVjMzICU+JSBkcGx5cjo6bXV0YXRlKGhwdjMzX3Bvc2l0aXZlID0gaWZfZWxzZShyZWFkcyA+IDIwLHRydWUgPSAgInBvc2l0aXZlIixmYWxzZSA9ICAibmVnYXRpdmUiKSkKaHB2MzNfcG9zaXRpdmUkcmVhZHMgPSBOVUxMCmFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBocHYzM19wb3NpdGl2ZSkKYGBgCgojIGNOTUYKYGBge3J9CmxpYnJhcnkocmV0aWN1bGF0ZSkKYGBgCgpgYGB7cn0KI3dyaXRlIGV4cHJlc3Npb24KYWNjMV9jYW5jZXJfY2VsbHMgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxuZmVhdHVyZXMgPSAyMDAwKQp2YXJnZW5lcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMpCmhtc2NfZXhwcmVzc2lvbiA9IHQoYXMubWF0cml4KEdldEFzc2F5RGF0YShhY2MxX2NhbmNlcl9jZWxscyxzbG90PSdkYXRhJykpKQpobXNjX2V4cHJlc3Npb24gPSAyKipobXNjX2V4cHJlc3Npb24gI2NvbnZlcnQgZnJvbSBsb2cyKHRwbSsxKSB0byB0cG0KaG1zY19leHByZXNzaW9uID0gaG1zY19leHByZXNzaW9uLTEKIyBobXNjX2V4cHJlc3Npb24gPSBobXNjX2V4cHJlc3Npb25bLCFjb2xTdW1zKGhtc2NfZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3coaG1zY19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMApobXNjX2V4cHJlc3Npb24gPSBobXNjX2V4cHJlc3Npb25bLHZhcmdlbmVzXQp3cml0ZS50YWJsZSh4ID0gaG1zY19leHByZXNzaW9uICxmaWxlID0gJy4vRGF0YS9jTk1GL2htc2NfZXhwcmVzc2lvbkRhdGFfMkt2YXJnZW5lcy50eHQnLHNlcCA9ICJcdCIpCmBgYAoKCgpgYGB7cHl0aG9uIGV2YWw9Rn0KZnJvbSBjbm1mIGltcG9ydCBjTk1GCm5hbWUgPSAnSE1TQ19jTk1GXzJLdmFyZ2VuZXMnCm91dGRpciA9ICcuL0RhdGEvY05NRicKS19yYW5nZSA9IG5wLmFyYW5nZSgzLDEwKQpjbm1mX29iaiA9IGNOTUYob3V0cHV0X2Rpcj1vdXRkaXIsIG5hbWU9bmFtZSkKY291bnRzX2ZuPScuL0RhdGEvY05NRi9obXNjX2V4cHJlc3Npb25EYXRhXzJLdmFyZ2VuZXMudHh0Jwp0cG1fZm4gPSBjb3VudHNfZm4gIyMgVGhpcyBpcyBhIHdlaXJkIGNhc2Ugd2hlcmUgYmVjYXVzZSB0aGlzIGRhdGFzZXQgaXMgbm90IDMnIGVuZCB1bWkgc2VxdWVuY2luZywgd2Ugb3B0ZWQgdG8gdXNlIHRoZSBUUE0gbWF0cml4IGFzIHRoZSBpbnB1dCBtYXRyaXggcmF0aGVyIHRoYW4gdGhlIGNvdW50IG1hdHJpeAoKY25tZl9vYmoucHJlcGFyZShjb3VudHNfZm49Y291bnRzX2ZuLCBjb21wb25lbnRzPUtfcmFuZ2UsIHNlZWQ9MTQsdHBtX2ZuPXRwbV9mbikKYGBgCgpgYGB7cHl0aG9uIGV2YWw9Rn0KY25tZl9vYmouZmFjdG9yaXplKHdvcmtlcl9pPTAsIHRvdGFsX3dvcmtlcnM9MSkKYGBgCgpgYGB7cHl0aG9uIGV2YWw9Rn0KY25tZl9vYmouY29tYmluZSgpCmNubWZfb2JqLmtfc2VsZWN0aW9uX3Bsb3QoKQpgYGAKIyMgU2F2ZSBvYmplY3QKYGBge3B5dGhvbiBldmFsPUZ9CmltcG9ydCBwaWNrbGUKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl8yS3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAnd2InKQpwaWNrbGUuZHVtcChjbm1mX29iaiwgZikKZi5jbG9zZSgpCmBgYAoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCmYgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfMkt2YXJnZW5lcy9jbm1mX29iai5wY2tsJywgJ3JiJykKY25tZl9vYmogPSBwaWNrbGUubG9hZChmKQpmLmNsb3NlKCkKYGBgCgoKYGBge3B5dGhvbn0Kc2VsZWN0ZWRfayA9IDQKZGVuc2l0eV90aHJlc2hvbGQgPSAwLjEKY25tZl9vYmouY29uc2Vuc3VzKGs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQsc2hvd19jbHVzdGVyaW5nPVRydWUpCnVzYWdlX25vcm0sIGdlcF9zY29yZXMsIGdlcF90cG0sIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCmBgYAoKYGBge3J9CiMgY2FsY3VsYXRlIHVzYWdlIGJ5IGV4cHJlc3Npb24qZ2VuZXMgY29lZnM6CmdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzCmFsbF9tZXRhZ2VuZXM9IHB5JHVzYWdlX25vcm0KYGBgCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGJ5IHRvcCAyMDAgZ2VuZXMgb2YgZWFjaCBwcm9ncmFtCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQpCiAgIAogIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0Cn0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbHRfbGlzdCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CiMgTWFrZSBtZXRhZ2VuZSBuYW1lcwpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlX21ldGFkYXRhID0gYWxsX21ldGFnZW5lcyAlPiUgc2VsZWN0KGkpCiAgYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKCmBgYAojIyBhc3NpZ25tZW50IFVNQVAKYGBge3J9Cmxhcmdlcl9ieSA9IDIKbWVzc2FnZShwYXN0ZSgibGFyZ2VyX2J5ID0gIiwgbGFyZ2VyX2J5KSkKYWNjMV9jYW5jZXJfY2VsbHMgPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLGxhcmdlcl9ieSA9IGxhcmdlcl9ieSxwcm9ncmFtX25hbWVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpCnNlbGVjdGVkX2sgPSBweSRzZWxlY3RlZF9rCmNvbG9ycyA9ICByYWluYm93KHNlbGVjdGVkX2spCmNvbG9ycyA9IGMoY29sb3JzLCJncmV5IikKRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLHB0LnNpemUgPSAyLGNvbHMgPWNvbG9ycykKYGBgIAoKIyMgQ29tcGFyaXNpb25zCmBgYHtyfQpjbnZfdnNfaHB2ID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJjbnYuY2x1c3RlciIsImhwdjMzX3Bvc2l0aXZlIikpCnRlc3QgPC0gZmlzaGVyLnRlc3QodGFibGUoY252X3ZzX2hwdikpCmdnYmFyc3RhdHMoCiAgY252X3ZzX2hwdiwgY252LmNsdXN0ZXIsIGhwdjMzX3Bvc2l0aXZlLAogIHJlc3VsdHMuc3VidGl0bGUgPSBGQUxTRSwKICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICJGaXNoZXIncyBleGFjdCB0ZXN0IiwgIiwgcC12YWx1ZSA9ICIsCiAgICBpZmVsc2UodGVzdCRwLnZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQodGVzdCRwLnZhbHVlLCAzKSkKICApCikKCmBgYAoKYGBge3J9CmNudl92c19jbm1mID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJwcm9ncmFtLmFzc2lnbm1lbnQiLCJjbnYuY2x1c3RlciIpKQpjbnZfdnNfY25tZiA9IGNudl92c19jbm1mICU+JSBkcGx5cjo6ZmlsdGVyKHByb2dyYW0uYXNzaWdubWVudCA9PSAiMSIgfHByb2dyYW0uYXNzaWdubWVudCA9PSAiMiIgKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNudl92c19jbm1mKSkKZ2diYXJzdGF0cygKICBjbnZfdnNfY25tZiwgcHJvZ3JhbS5hc3NpZ25tZW50LCBjbnYuY2x1c3RlciwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKCmBgYHtyfQpjbm1mX3ZzX2hwdiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygicHJvZ3JhbS5hc3NpZ25tZW50IiwiaHB2MzNfcG9zaXRpdmUiKSkKY25tZl92c19ocHYgPSBjbm1mX3ZzX2hwdiAlPiUgZHBseXI6OmZpbHRlcihwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjEiIHxwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjIiICkKdGVzdCA8LSBmaXNoZXIudGVzdCh0YWJsZShjbm1mX3ZzX2hwdikpCmdnYmFyc3RhdHMoCiAgY25tZl92c19ocHYsIHByb2dyYW0uYXNzaWdubWVudCwgaHB2MzNfcG9zaXRpdmUsCiAgcmVzdWx0cy5zdWJ0aXRsZSA9IEZBTFNFLAogIHN1YnRpdGxlID0gcGFzdGUwKAogICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgIGlmZWxzZSh0ZXN0JHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZXN0JHAudmFsdWUsIDMpKQogICkKKQoKYGBgCgpgYGB7cn0KbXliX3ZzX2NudiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiY252LmNsdXN0ZXIiLCJNWUIiKSkKbXliX3ZzX2NudiAkY252LmNsdXN0ZXIgPSBhcy5jaGFyYWN0ZXIobXliX3ZzX2NudiAkY252LmNsdXN0ZXIgKQoKZ2dib3hwbG90KG15Yl92c19jbnYsIHggPSAiY252LmNsdXN0ZXIiLCB5ID0gIk1ZQiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCIxIiwiMiIpKSkKYGBgCgpgYGB7cn0KbXliX3ZzX2hwdiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiaHB2MzNfcG9zaXRpdmUiLCJNWUIiKSkKbXliX3ZzX2hwdiAkaHB2MzNfcG9zaXRpdmUgPSBhcy5jaGFyYWN0ZXIobXliX3ZzX2hwdiAkaHB2MzNfcG9zaXRpdmUgKQoKZ2dib3hwbG90KG15Yl92c19ocHYsIHggPSAiaHB2MzNfcG9zaXRpdmUiLCB5ID0gIk1ZQiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJwb3NpdGl2ZSIsIm5lZ2F0aXZlIikpKQpgYGAKCgoKYGBge3J9CmhwdlJlYWRzX3ZzX215YiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiSFBWMzMucmVhZHMiLCJNWUIiKSkKY29yciA9IGNvcihocHZSZWFkc192c19teWIkSFBWMzMucmVhZHMsaHB2UmVhZHNfdnNfbXliJE1ZQikKcHJpbnQoImNvcnJlbGF0aW9uIG9mIE1ZQiBhYmQgaHB2MzNfcmVhZHM6IiAlPiUgcGFzdGUoY29yciAlPiUgcm91bmQoZGlnaXRzID0gMikpICkKYGBgCgoK